package cn.swenty.backstage.framework.database.plugin;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

import cn.swenty.backstage.framework.database.entity.Entity;
import cn.swenty.backstage.framework.database.mybatis.enums.CRUD;
import cn.swenty.backstage.framework.database.mybatis.enums.DATABASE;
import cn.swenty.backstage.framework.database.mybatis.sql.SqlHelp;
import cn.swenty.backstage.framework.util.ReflectHelper;

/**
 * Mybatis的核心CRUD插件，通过拦截StatementHandler的prepare方法来实现。
 * 
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class CRUDPlugin implements Interceptor {
	
	private String dialect;

	public String getDialect() {
		return dialect;
	}

	public void setDialect(String dialect) {
		this.dialect = dialect;
	}

	private static final String FIELD_DELEGATE = "delegate";

	private static final String FIELD_MAPPEDSTATEMENT = "mappedStatement";

	private static final String FIELD_SQL = "sql";

	private static final String CONTENT_MAPPER = ".BasicMapper.";
	
	private static final String FIELD_ARAMETERMAPPINGS = "parameterMappings";

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(invocation.getTarget(),
				FIELD_DELEGATE);
		MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate,
				FIELD_MAPPEDSTATEMENT);
		Connection connection = (Connection) invocation.getArgs()[0];
		BoundSql boundSql = delegate.getBoundSql();
		//获取
		Object parameterObject = boundSql.getParameterObject();
			
		String sqlId = mappedStatement.getId();
		sqlId = sqlId.substring(sqlId.lastIndexOf(".") + 1);
		
		String sql = boundSql.getSql();
		
		if(parameterObject instanceof Entity){
			Entity nt = (Entity)parameterObject;
			switch (CRUD.get(sqlId)) {
			case QUERY:
				if(nt.get(Entity.PAGE_VIEW) != null){
					//统计sql
					String countSql = SqlHelp.getSelectTotalSql(SqlHelp.selectSql(nt), DATABASE.get(dialect));
					
					//获取统计的数量
					int count= queryInt(SqlHelp.buildSql(countSql ,nt),SqlHelp.bulidSqlParm(countSql,mappedStatement.getConfiguration(), nt), nt, mappedStatement, connection);
					
					//重新设置pagesize
					((PageView)nt.get(Entity.PAGE_VIEW)).setRowCount(count);
					
					//获取查询pagesql
					sql = SqlHelp.selectPageSql(DATABASE.get(dialect), nt);
				}else{
					sql = SqlHelp.selectSql(nt);
				}
				break;
			case SAVE:
				sql = SqlHelp.insertSql(nt);
				break;
			case UPDATE:
				sql = SqlHelp.updateSql(nt);
				break;
			case UPDATE_FULL:
				sql = SqlHelp.updateFullSql(nt);
				break;
			case DELETE:
				sql = SqlHelp.deleteSql(nt);
				break;
			default:
				break;
			}
			List<ParameterMapping> pars = SqlHelp.bulidSqlParm(sql,mappedStatement.getConfiguration(), nt);
			String buildSql = SqlHelp.buildSql(sql,nt);
			ReflectHelper.setValueByFieldName(boundSql, FIELD_SQL, buildSql);
			ReflectHelper.setValueByFieldName(boundSql, FIELD_ARAMETERMAPPINGS, pars);
		}
		
		return invocation.proceed();
		
	}
	
	

	private Integer queryInt(String sql, List<ParameterMapping> parameterMappings,Object parameterObject, MappedStatement mappedStatement, Connection connection) throws SQLException {
		BoundSql totalBoundSql = new BoundSql(mappedStatement.getConfiguration(), sql, parameterMappings,
				parameterObject);
		ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
				totalBoundSql);

		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = connection.prepareStatement(sql);
			parameterHandler.setParameters(pstmt);
			
			rs = pstmt.executeQuery();
			if (rs.next()) {
				return rs.getInt(1);
			}
		} finally {
			if (rs != null) {
				rs.close();
			}
			if (pstmt != null) {
				pstmt.close();
			}
		}
		return null;
	}
	

	@Override
	public Object plugin(Object target) {
		if (target instanceof RoutingStatementHandler) {
			BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(target,
					FIELD_DELEGATE);
			MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate,
					FIELD_MAPPEDSTATEMENT);
			BoundSql boundSql = delegate.getBoundSql();
			Object parameterObject = boundSql.getParameterObject();
			if (parameterObject instanceof Entity && delegate.getBoundSql().getParameterObject() != null
					&& mappedStatement.getId().contains(CONTENT_MAPPER)) {
				return Plugin.wrap(target, this);
			}
		}
		return target;
	}

	@Override
	public void setProperties(Properties properties) {
		dialect = properties.getProperty("dialect");
	}

}
